/* Standard Includes */
#include <string.h>

/* Local Includes */
#include "std_list.h"

/**
 * A node in the linked list.
 */
struct _std_list_node {
	/**
	 * The data stored in the node.
	 */ 
	void *data;

	/**
	 * The next node in the list, or NULL.
	 */
    struct _std_list_node *next;

	/**
	 * The previous node in the list, or NULL.
	 */
    struct _std_list_node *prev;
};

/**
 * The doubly linked list context.
 *
 * This stores the begin and end (one past last node) of the list,
 * and can store any state data associated with a list context.
 */
struct _std_list {
	/**
	 * A pointer to the first node in the list.
	 *
	 * If the list is empty, this points to end.
	 */
    struct _std_list_node *begin;

	/**
	 * A pointer to the node after the last node in the list.
	 * If the list is empty, this points to an empty node.
	 */
    struct _std_list_node *end;
	
	/**   
	 * The size of the list.
	 */
	int length;

	/**
	 * A destroy function used to free the user's data before a list
	 * item is free'd.
	 */
	STDDestroyNotify destroy_func;
};

/* Functions */
/* --------- */

/**
 * Allocate a single std_list_node.
 *
 * @return
 * A new list on success, or NULL on error
 */
static struct _std_list_node* std_list_node_create (void)
{
  struct _std_list_node *list_node;
  
  list_node = malloc ( sizeof ( struct _std_list_node ));

  if ( !list_node )
	  return NULL;

  list_node->data = NULL;
  list_node->next = NULL;
  list_node->prev = NULL;
  
  return list_node;
}

/**
 * Destroy a single std_list_node.
 *
 * @return
 * 0 on success, or -1 on error.
 */
static int std_list_node_destroy ( 
		struct _std_list_node *list_node,
        STDDestroyNotify destroy_func ) {

	if ( !list_node )
		return -1;

	if ( destroy_func )
	    destroy_func ( list_node->data );

	list_node->data = NULL;
	list_node->next = NULL;
	list_node->prev = NULL;
	free ( list_node );
	list_node = NULL;
	
	return 0;
}

/**
 * Allocate for a new list.
 *
 * @return
 * A new list on success, or NULL on error
 */
struct _std_list* std_list_create ( STDDestroyNotify destroy_func )
{
  struct _std_list *list;
  
  list = malloc ( sizeof ( struct _std_list ));

  if ( !list )
	  return NULL;

  list->begin = std_list_node_create ();

  if ( !list->begin )
	  return NULL;

  list->end = list->begin;
  list->length = 0;
  list->destroy_func = destroy_func;
  
  return list;
}

int std_list_remove_all ( struct _std_list *list ) {
	std_list_iterator iter;

	/* Traverse the list and free the data members */
	for ( iter = std_list_begin ( list ); 
		  iter != std_list_end ( list ); ){ 
		iter = std_list_remove ( list, iter );

	    if (!iter )
			return -1;
	}
	
	return 0;
}

int std_list_destroy ( struct _std_list *list ) {

	if ( !list )
		return -1;


	if ( std_list_remove_all ( list ) == -1 )
		return -1;

    /* Free the dummy node (remove won't do this) */
    std_list_node_destroy(list->end, list->destroy_func);   
    
    /* Free the list structure */
    free(list); 
	
	return 0;
}

int std_list_append ( 
    struct _std_list *list,
    void *data ) {

    if ( !list )
        return -1;

	if ( std_list_insert ( list, list->end, data ) == -1 )
		return -1;

    return 0;
}

int std_list_prepend ( 
    struct _std_list *list,
    void *data ) {

    if ( !list )
        return -1;

	if ( std_list_insert ( list, list->begin, data ) == -1 )
		return -1;

    return 0;
}

int std_list_insert (
	struct _std_list *list,
	const std_list_iterator iter,
	void *data) {

	std_list_iterator new_node;

	if ( !list )
		return -1;

	if ( !iter )
		return -1;

	new_node = std_list_node_create ();

	if ( !new_node )
		return -1;

	new_node->data = data;

	if ( iter == list->begin ) {
		new_node->next = iter;
		iter->prev = new_node;
		list->begin = new_node;
	} else { 
		std_list_iterator before;
		before = iter->prev;

		new_node->next = iter;
		before->next = new_node;

		new_node->prev = before;
		iter->prev = new_node;
	}

	list->length = list->length + 1;

	return 0;
}

int std_list_insert_sorted (
	struct _std_list *list,
	void *data,
	const STDCompareFunc func) {

    std_list_iterator current;
    void *current_data;

	if ( !list )
		return -1;

	if ( !func )
		return -1;

    /* Seek to the corrent position to insert new item */
    for (current  = std_list_begin(list); 
         current != std_list_end(list);
         current  = std_list_next(current))
    {
        if (std_list_get_data(current, &current_data) != 0){
            return -2;
        }

        /* If the new data is <= data at this node, stop looping and
         * insert the data into the list before the current. */
        if (func(data, current_data) <= 0){
            break;
        }
    }

    /* Insert the item before the current iterator now. */
    return std_list_insert(list, current, data);
}

std_list_iterator std_list_remove (
	struct _std_list *list,
	std_list_iterator iter ) {

	std_list_iterator after;

	if ( !list )
		return NULL;

	if ( !iter )
		return NULL;

	/* remove the iterator */
	if ( iter == list->end )
		return NULL;

	if ( iter == list->begin ) {
		after = iter->next;
		list->begin = after;
		list->begin->prev = NULL;
	} else {
		std_list_iterator before;	

		before = iter->prev;
		after = iter->next;

		before->next = after;
		after->prev = before;
	}

	if ( std_list_node_destroy ( iter, list->destroy_func ) == -1 )
		return NULL;

	list->length -= 1;

	return after;
}

std_list_iterator std_list_find (
	const struct _std_list *list,
	const void *data,
	const STDCompareFunc func ) {
	std_list_iterator iter;
	std_list_iterator found;

	if ( !list )
		return NULL;

	if ( !func )
		return NULL;

	found = std_list_end ( list );

	/* Find the item */
	for ( iter = std_list_begin ( list ); 
		  iter != std_list_end ( list ); 
		  iter = std_list_next ( iter ) ){ 
		void *iter_data;

		if ( std_list_get_data ( iter, &iter_data ) == -1 )
			return NULL;
	
		if ( func ( iter_data, data ) == 0 ) {
			found = iter;
			break;
		}
	}

	return iter;
}

std_list_iterator std_list_begin ( const struct _std_list *list ) {

	if ( !list ) 
		return NULL;

	return list->begin;
}

std_list_iterator std_list_end ( const struct _std_list *list ) {

	if ( !list ) 
		return NULL;

	return list->end;
}

std_list_iterator std_list_next ( const std_list_iterator iter ){
	if ( !iter ) 
		return NULL;

	return iter->next;
}

std_list_iterator std_list_previous ( const std_list_iterator iter ){
	if ( !iter ) 
		return NULL;

	return iter->prev;
}

int std_list_length ( struct _std_list *list ) {

	if ( !list )
		return -1;

	return list->length;
}

int std_list_foreach ( 
	const struct _std_list *list,
	const STDFunc func,
	void *user_data ) {
	std_list_iterator iter;

	if ( !list )
		return -1;

	if ( !func )
		return -1;

	/* do the foreach */
	for ( iter = std_list_begin ( list ); 
		  iter != std_list_end ( list ); 
		  iter = std_list_next ( iter ) ){ 
		void *data;

		if ( std_list_get_data ( iter, &data ) == -1 )
			return -1;
		
		if ( func ( data, user_data ) == 0 )
			break;
	}

	return 0;
}

int std_list_sort (
	struct _std_list *list,
	STDCompareFunc compare_func ) {

	if ( !list )
		return -1;

	if ( !compare_func )
		return -1;

	/* sort the list */

	return 0;
}

int std_list_sort_with_data (
	struct _std_list *list,
	STDCompareDataFunc compare_func,
	void *user_data ) {
	if ( !list )
		return -1;

	if ( !compare_func )
		return -1;

	/* sort the list */

	return 0;
}

int std_list_get_data ( 
    std_list_iterator iter,
    void *data	) {

	if ( !iter )
		return -1;

    /* Copy the pointer */
    memcpy(data, &iter->data, sizeof(void *));

	return 0;
}
